package com.dragon.blelibrary.manager;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.os.Message;

import com.dragon.blelibrary.bleutils.BleLOG;
import com.dragon.blelibrary.events.BleEventType;
import com.dragon.blelibrary.events.BlePropertyObservable;
import com.dragon.blelibrary.lib.DispatchMessages;
import com.dragon.blelibrary.lib.GattCallbackOut;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

class MyBleManager extends BleManager<ManagerCallbacks>{
    private String tag = "FendaBleManager";
    //是否使能所有的通知（默认否）
    private boolean enableAllNotify = false;
    private DispatchMessages messages;   //接口
    //ble接口事件外调
    private GattCallbackOut gattCallbackOut;
    /**必须要使能的通知*/
    private HashMap<String, BluetoothGattCharacteristic> mustNotify = new HashMap<>();
    public MyBleManager(Context context) {
        super(context);
    }

    @Override
    protected BleManagerGattCallback getGattCallback() {

        return mGattCallback;
    }

    /**
     * 设置是否自动使能所有通知（默认不使能）
     * @param enable
     */
    public void setEnableAllNotify(boolean enable){
        this.enableAllNotify = enable;
    }
    public void setGattCallbackOut(GattCallbackOut callbackOut){
        this.gattCallbackOut = callbackOut;
    }
    /**
     * 设置消息处理接口外调
     * @param msg
     */
    public void setMessages(DispatchMessages msg){
        this.messages = msg;
    }


    /**
     * 判断是否存在这个服务UUID
     * @param serviceUUid 需要判断是否存在的服务UUID 一般ota升级时有唯一UUID这时可用
     * @return boolean
     */
    public boolean isExistUUID(UUID serviceUUid){
        BluetoothGatt gatt = mBluetoothGatt;
        if (gatt != null){
            BluetoothGattService service = gatt.getService(serviceUUid);
            if(service != null){
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 使能通知
     * @param serviceUUid
     * @param notifiUUID
     * @param isEnable
     * @return
     */
    public boolean enableNotification(UUID serviceUUid,UUID notifiUUID,boolean isEnable){
        BluetoothGatt gatt = mBluetoothGatt;
        if(gatt != null){
            BluetoothGattService service = gatt.getService(serviceUUid);
            if(service != null){
                BluetoothGattCharacteristic notifiCharacteristic = service.getCharacteristic(notifiUUID);
                if(notifiCharacteristic != null){
                    Request requset = null;

                    int props = notifiCharacteristic.getProperties();

                    if(isEnable){
                        if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                            requset = Request.newEnableNotificationsRequest(notifiCharacteristic);
                        }else if((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0){

                            requset = Request.newEnableIndicationsRequest(notifiCharacteristic);
                        }
//                        requset =Request.newEnableNotificationsRequest(notifiCharacteristic);
                    }else{

                        if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                            requset = Request.newDisableNotificationRequest(notifiCharacteristic);
                        }else if((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0){

//                            requset = Request.newEnableIndicationRequest(notifiCharacteristic);
                        }

//                        requset =Request.newDisableNotificationRequest(notifiCharacteristic);
                    }
                    boolean requsetResult = enqueue(requset);
                    if(!requsetResult){
                        BleLOG.e(tag,"enableNotification enqueue = false");
                    }
                    return requsetResult;
                }else{
                    BleLOG.e(tag,"enableNotification notifiCharacteristic = null");
                }
            }else{
                BleLOG.e(tag,"enableNotification service = null");
            }
        }else{
            BleLOG.e(tag,"enableNotification gatt = null");
        }
        return false;
    }
    private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
        /**
         *连接参数
         */
        @Override
        protected void onConnectionUpdated(int interval, int latency, int timeout) {
            super.onConnectionUpdated(interval, latency, timeout);
            if(gattCallbackOut != null)
                gattCallbackOut.onConnectionUpdated(interval, latency, timeout);
        }

        /**
         * 寻找服务,通知
         * @param gatt the gatt device with services discovered
         * @return
         */
        @Override
        protected boolean isRequiredServiceSupported(BluetoothGatt gatt) {
            findAllNotifyCharacteristic(gatt);
            return true;
        }

        /**
         * gatt 初始化需要做的事情(使能通知)
         * @param gatt the gatt device with services discovered
         * @return
         */
        @Override
        protected Deque<Request> initGatt(BluetoothGatt gatt) {
            mHandler.removeMessages(MyHandlerCallback.handler_discoverServices_time_out);
//            mHandler.sendEmptyMessageDelayed(MyHandlerCallback.handler_enable_notify_time_out,40 * 1000);
            Message msg = mHandler.obtainMessage();
            msg.what = MyHandlerCallback.handler_enable_notify_time_out;
            msg.obj = mustNotify;
            mHandler.removeMessages(MyHandlerCallback.handler_enable_notify_time_out);
            mHandler.sendMessageDelayed(msg,40 * 1000);
            final LinkedList<Request> requests = new LinkedList<>();
            List<UUID[]> listUUID;
            if(enableAllNotify){//自动使能通知
                listUUID = GattServiceCharacteristic.getAutoNotificationMap();
            }else{//手动使能
                listUUID = GattServiceCharacteristic.getNotificationMap();
            }
            if(listUUID != null && !listUUID.isEmpty()) {
                for (UUID[] arrUUid : listUUID) {
                    if (arrUUid != null && arrUUid.length == 2) {
                        BluetoothGattService service = gatt.getService(arrUUid[0]);
                        if (service != null) {
                            BluetoothGattCharacteristic notifiCharacteristic = service.getCharacteristic(arrUUid[1]);
                            if (notifiCharacteristic != null) {
                                BluetoothGattDescriptor descriptor = notifiCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);

                                int props = notifiCharacteristic.getProperties();
                                if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                                    if(descriptor != null)
                                        mustNotify.put(notifiCharacteristic.getUuid().toString(),notifiCharacteristic);

                                    Request requset = Request.newEnableNotificationsRequest(notifiCharacteristic);
                                    requests.add(requset);
                                }else if((props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0){
                                    if(descriptor != null)
                                        mustNotify.put(notifiCharacteristic.getUuid().toString(),notifiCharacteristic);

                                    Request requset = Request.newEnableIndicationsRequest(notifiCharacteristic);
                                    requests.add(requset);
                                }

                            }
                        }
                    }
                }
            }
            return requests;
        }

        /**
         * ble已经连接，此时添加一个计时，如果多少秒内没有完成发现服务的话，就断开
         * @param gatt
         */
        @Override
        protected void onDeviceConnected(BluetoothGatt gatt) {
            super.onDeviceConnected(gatt);
            Message message = mHandler.obtainMessage();
            message.what = MyHandlerCallback.handler_discoverServices_time_out;
            message.obj = gatt.getDevice();
            mHandler.sendMessageDelayed(message,30 * 1000);
        }

        @Override
        protected void onDeviceReady() {
            if(mustNotify == null || mustNotify.isEmpty()){
                mHandler.removeMessages(MyHandlerCallback.handler_enable_notify_time_out);
                mCallbacks.onConnectionNotificationReturn(mBluetoothGatt.getDevice());//通知使能完毕
            }else{
                final LinkedList<Request> requests = new LinkedList<>();
                Set<String> keys = mustNotify.keySet();
                for (String key:keys) {
                    BluetoothGattCharacteristic characteristic = mustNotify.get(key);
                    if(characteristic != null){
                        Request requset = Request.newEnableNotificationsRequest(characteristic);
                        requests.add(requset);
                    }
                }
                enAllNotify(requests);
            }
        }

        @Override
        protected void onOneNotificationsEnabled(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
            if(mustNotify != null && !mustNotify.isEmpty()){
                try {
                    String uuid = descriptor.getCharacteristic().getUuid().toString();
                    mustNotify.remove(uuid);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        /**
         * 读取每个使能完的通知
         * @param status
         * @param gatt       GATT client
         * @param descriptor Descriptor that was read from the associated remote device.
         */
        @Override
        protected void onDescriptorRead(int status, BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {

        }

        /**
         * 断开
         */
        @Override
        protected void onDeviceDisconnected() {

        }
        //////////////////////系统接口，必须先运行父类的 start////////////////////////////////////////////////////////////
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if(gattCallbackOut != null)
                gattCallbackOut.onConnectionStateChange(gatt,status,newState);
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if(gattCallbackOut != null)
                gattCallbackOut.onServicesDiscovered(gatt,status);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            if(gattCallbackOut != null)
                gattCallbackOut.onDescriptorRead(gatt,descriptor,status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            if(gattCallbackOut != null)
                gattCallbackOut.onDescriptorWrite(gatt,descriptor,status);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if(messages != null){
                messages.onDispatchMessages(
                        characteristic.getService().getUuid()
                        ,characteristic.getUuid()
                        ,gatt.getDevice().getAddress()
                        ,characteristic.getValue());
            }
            if(gattCallbackOut != null)
                gattCallbackOut.onCharacteristicRead(gatt,characteristic,status);
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if(gattCallbackOut != null)
                gattCallbackOut.onCharacteristicWrite(gatt,characteristic,status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            if(messages != null){
                messages.onDispatchMessages(
                        characteristic.getService().getUuid()
                        ,characteristic.getUuid()
                        ,gatt.getDevice().getAddress()
                        ,characteristic.getValue());
            }
            if(gattCallbackOut != null)
                gattCallbackOut.onCharacteristicChanged(gatt,characteristic);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            if(gattCallbackOut != null)
                gattCallbackOut.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            if(gatt != null && gatt.getDevice() != null)
                BlePropertyObservable.getInstance().fireEvent(BleEventType.MTU, gatt.getDevice().getAddress(),mtu);
        }
        //////////////////////系统接口，必须先运行父类的 end////////////////////////////////////////////////////////////
    };
    ////////////////////////////////////////////////////////////////////////////////////////////
    //数据发送
    //
    //
    ///////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 发送数据
     * @param value
     * @param serviceUUid
     * @param characteristicUuid
     * @param isResponse 是否带回应
     * @return
     */
    public boolean sendMessage(byte[] value,UUID serviceUUid,UUID characteristicUuid,boolean isResponse){
        BluetoothGatt gatt = mBluetoothGatt;
        if(gatt != null){
            BluetoothGattService service = gatt.getService(serviceUUid);
            if(service != null){
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
                if(characteristic != null){
                    int writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
                    if(!isResponse){
                        writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE;
                    }
                    Request request = Request.newWriteRequest(characteristic, value, writeType);
                    boolean enqueue = enqueue(request);
                    if(!enqueue){
                        BleLOG.e(tag,"sendMessage enqueue = false");
                    }
                    return enqueue;
                }else{
                    BleLOG.e(tag,"sendMessage characteristic = null");
                }
            }else{
                BleLOG.e(tag,"sendMessage service = null");
            }
        }else{
            BleLOG.e(tag,"sendMessage gatt = null");
        }
        return false;
    }

    /**
     * 以读的方式发送数据
     * @param value
     * @param serviceUUid
     * @param characteristicUuid
     * @param isResponse 是否带回应
     * @return
     */
    public boolean sendMessageRead(byte[] value,UUID serviceUUid,UUID characteristicUuid,boolean isResponse){
        BluetoothGatt gatt = mBluetoothGatt;
        if(gatt != null){
            BluetoothGattService service = gatt.getService(serviceUUid);
            if(service != null){
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
                if(characteristic != null){
                    int writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
                    if(!isResponse){
                        writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE;
                    }
                    characteristic.setWriteType(writeType);
                    characteristic.setValue(value);
                    Request request = Request.newReadRequest(characteristic);
                    boolean enqueue = enqueue(request);
                    if(!enqueue){
                        BleLOG.e(tag,"sendMessageRead enqueue = false");
                    }
                    return enqueue;
                }else{
                    BleLOG.e(tag,"sendMessageRead characteristic = null");
                }
            }else{
                BleLOG.e(tag,"sendMessageRead service = null");
            }
        }else{
            BleLOG.e(tag,"sendMessageRead gatt = null");
        }
        return false;
    }


    //寻找所有的通知
    private void findAllNotifyCharacteristic(BluetoothGatt gatt){
        if(gatt != null){
            List<BluetoothGattService> listServices = gatt.getServices();
            if(listServices != null && !listServices.isEmpty()){
                for (BluetoothGattService server:listServices) {
                    if(server != null){
                        List<BluetoothGattCharacteristic> listCh = server.getCharacteristics();
                        if(listCh != null && !listCh.isEmpty()){
                            for (BluetoothGattCharacteristic ch:listCh) {
                                int props = ch.getProperties();
                                if ((props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0 || (props & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
                                    GattServiceCharacteristic.putAutoNotificationMap(server.getUuid(),ch.getUuid());
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 请求mtu大小
     * @param mtu
     * @return
     */
    public boolean requestFendaMtu(int mtu){
        Request request = Request.newMtuRequest(mtu);
        boolean enqueue = enqueue(request);
        if(!enqueue){
            BleLOG.e(tag,"requestMtu enqueue = false");
        }
        return enqueue;
    }
}
